<!-- <template>
  <view class="test">
    <view>item</view>
    <text class="icon-bluetooth"></text>
    <button @click="onTest">获取文件</button>
    <button @click="download">下载</button>
    <button @click="read">读取</button>
  </view>
</template>

<script setup>
import { ref } from 'vue'

function onTest() {
  uni.getSavedFileList({
    success: function (res) {
      console.log(res.fileList)
    },
  })
}
function download() {
  uni.downloadFile({
    url: 'https://gitee.com/dpaul/dtwapp/blob/master/src/pages.json', //仅为示例，并非真实的资源
    success: (res) => {
      if (res.statusCode === 200) {
        console.log('下载成功', res)
        uni.saveFile({
          tempFilePath: res.tempFilePath,
          success: function (file) {
            // var savedFilePath = res.savedFilePath;
            console.log('file:', file)
          },
        })
      }
    },
  })
}
function read() {
  readAndroidAbsolutePath('_doc/uniapp_save/17555069773341.json')
}
function readAndroidAbsolutePath(absolutePath) {
  // 转换路径为本地 URL（兼容 Android 10+）
  return new Promise((resolve, reject) => {
    plus.io.resolveLocalFileSystemURL(
      absolutePath,
      (entry) => {
        entry.file(
          (file) => {
            const reader = new plus.io.FileReader()
            reader.onloadend = (e) => {
              console.log('文件内容:', e)
              // const model = /.*base64,/
              // const newFile = e.target.result.replace(model, '')
              // // 解析base64
              // const text = decodeURIComponent(escape(atob(newFile)))
              // console.log('text:', text)

              resolve(e)
            }
            reader.readAsText(file) // 读取为文本
            // reader.readAsDataURL(file)
          },
          (err) => {
            console.error('读取文件失败:', err.message)
            reject(err)
          },
        )
      },
      (err) => {
        console.error('路径解析失败:', err.message)
        reject(err)
        // 常见错误：权限不足或路径不存在
      },
    )
  })
}
</script>

<style lang="scss" scoped></style> -->
<template>
  <button class="test" @click="openBlueTooth">test</button>
  <button class="test" @click="scanBlueTooth">开始扫描设备</button>
  <button @click="closeBlueTooth">关闭蓝牙</button>
  <button @click="close">断开连接</button>
  <!-- <button @click="initTork">获取扭矩扳手的范围</button>
  <button @click="getTorque">获取扭矩</button>
  <button @click="getInfo">信息</button> -->
  <button @click="connectDevice({ deviceId: 'B0:78:39:8B:FE:37' })">连接</button>
  <button @click="send">发送指令</button>
  <button @click="onNotify">订阅</button>
  <button @click="onListen">监听</button>
  <button @click="setBLEMTU">设置最大传输单元</button>
  <input class="uni-input" focus placeholder="自动获得焦点" v-model="cmd" />
  <view class="info">{{ data }}</view>
  <button @click="getData">实时数据</button>
  <button @click="setData">设置心跳数据</button>
  <view class="list">
    <view class="list-item" v-for="(item, index) in filterRes" :key="index">
      <view>蓝牙设备名称：{{ item.name }}</view>
      <view>蓝牙设备id：{{ item.deviceId }}</view>
      <view>蓝牙设备信号强度：{{ item.RSSI }}</view>
      <view>当前蓝牙设备的广播数据段中的 LocalName 数据段：{{ item.localName }}</view>
      <view>当前蓝牙设备的广播数据段中的 ServiceUUIDs 数据段：{{ item.advertisServiceUUIDs }}</view>
      <button @click="connectDevice(item)">连接</button>
      <view>-----------------</view>
    </view>
  </view>
</template>

<!-- <script module="vconsole" lang="renderjs">  
   import VConsole from 'vconsole' // TODO
   new VConsole() // 使用vconsole
</script> -->

<script setup>
import { onLoad } from '@dcloudio/uni-app'
import { ref } from 'vue'

function setBLEMTU() {
  uni.setBLEMTU({
    deviceId: opts.notify.deviceId,
    mtu: 255,
    success: (success) => {
      console.log('设置成功', success)
    },
  })
}

const opts = {
  read: {
    characteristicId: '0000FFF3-0000-1000-8000-00805F9B34FB',
    deviceId: 'B0:78:39:8B:FE:37',
    serviceId: '0000FFF0-0000-1000-8000-00805F9B34FB',
  },
  write: {
    characteristicId: '0000FFF2-0000-1000-8000-00805F9B34FB',
    deviceId: 'B0:78:39:8B:FE:37',
    serviceId: '0000FFF0-0000-1000-8000-00805F9B34FB',
  },
  notify: {
    characteristicId: '0000FFF1-0000-1000-8000-00805F9B34FB',
    deviceId: 'B0:78:39:8B:FE:37',
    serviceId: '0000FFF0-0000-1000-8000-00805F9B34FB',
  },
}
// const opts = {
//   write: {
//     characteristicId: '0000FFF2-0000-1000-8000-00805F9B34FB',
//     deviceId: 'A2:22:03:29:01:E4',
//     serviceId: '0000FFF0-0000-1000-8000-00805F9B34FB',
//   },
//   notify: {
//     characteristicId: '0000FFF1-0000-1000-8000-00805F9B34FB',
//     deviceId: 'A2:22:03:29:01:E4',
//     serviceId: '0000FFF0-0000-1000-8000-00805F9B34FB',
//   },
// }

const filterRes = ref([])
// 打开蓝牙
function openBlueTooth() {
  let main, Context, BManager, BAdapter
  main = plus.android.runtimeMainActivity()
  Context = plus.android.importClass('android.content.Context')
  BManager = main.getSystemService(Context.BLUETOOTH_SERVICE)
  plus.android.importClass(BManager) //引入相关的method函数
  BAdapter = BManager.getAdapter()
  plus.android.importClass(BAdapter) //引入相关的method函数，这样之后才会有isEnabled函数支持
  if (!BAdapter.isEnabled()) {
    BAdapter.enable()
  }
  console.log('打开蓝牙')
}

// 扫描蓝牙设备
async function scanBlueTooth() {
  try {
    await uni.stopBluetoothDevicesDiscovery()
  } catch (error) {
    console.log(error)
  }
  // 打开蓝牙适配
  uni.openBluetoothAdapter({
    async success() {
      // 获取本机蓝牙适配器状态。
      let status = await uni.getBluetoothAdapterState()
      if (!status.available) {
        // _this.setData({ status: 1 })
        uni.showModal({
          title: '请确认手机蓝牙是否打开',
          showCancel: false,
        })
        openBlueTooth()
        return
      }

      // _this.setData({ status: 2 })
      // 搜索设备
      await uni.startBluetoothDevicesDiscovery()
      initBlueTooth()
    },
    async fail(err) {
      console.log(err)

      if (err.errMsg === 'openBluetoothAdapter:fail already opened') {
        // _this.setData({ status: 2 })
        // 搜索设备
        await uni.startBluetoothDevicesDiscovery()

        initBlueTooth()
      } else {
        // _this.setData({ statusText: '蓝牙适配失败！原因：微信未给用户授权；或者系统未给微信授权' })
        console.log('蓝牙适配失败！原因：微信未给用户授权；或者系统未给微信授权', err)
      }
      if ([10001].includes(err.code)) {
        uni.showToast({
          title: '请确认手机蓝牙是否打开',
          icon: 'none',
          duration: 3000,
        })
        openBlueTooth()
      }
    },
  })
}

async function initBlueTooth() {
  // let _this = this
  // wx.removeStorageSync('curDevice')
  // this.setData({ curDevice: null })
  uni.showLoading({
    // mask: true,
    title: '设备扫描中',
  })
  // 获取已经链接的设备
  let devices
  let time = 3
  let timer = setInterval(async () => {
    time--
    if (time >= 0) {
      devices = await uni.getBluetoothDevices()
    } else {
      // 停止搜索
      uni.stopBluetoothDevicesDiscovery()
      clearInterval(timer)
      uni.hideLoading()
      // filterRes.value = devices.devices.filter((v) => !v.name.includes('未知'))
      filterRes.value = devices.devices
      // let filterRes = [{name: "UAF028", deviceId: "FD:B9:00:B5:68:39", RSSI: -83}];
      console.log('filterRes:', filterRes.value)
      // 没找到，进行提示
      if (filterRes.value.length == 0) {
        uni.showModal({
          title: '提示',
          content:
            '没有找到附近的设备，请确认扳手是否开机。如果扳手已经开机，请点击【重新扫描】按钮重新扫描',
          showCancel: false,
        })
        // _this.setData({ status: 3 })
      }
    }
  }, 1000)
}

function closeBlueTooth() {
  uni.closeBluetoothAdapter({
    success: function (res) {
      console.log('关闭蓝牙成功')
    },
  })
}
async function close() {
  uni.closeBLEConnection({
    deviceId: opts.read.deviceId,
    success: function (res) {
      console.log('断开蓝牙成功')
    },
  })
}
function ab2hex(buffer) {
  var hexArr = Array.prototype.map.call(new Uint8Array(buffer), function (bit) {
    return ('00' + bit.toString(16)).slice(-2)
  })
  // console.log('ab2hex: ', hexArr)
  return hexArr
}
const DeviceClass = (function () {
  let instance = null
  class Device {
    constructor() {
      if (instance) {
        return instance
      }
      // 初始化蓝牙相关的状态或变量
      // this.bluetoothStatus = null // 蓝牙状态
      this.optsNotify = [] //用于防止重复启动notify功能
      // this.notifyCount = 0 //用于判断启动了几次notify功能
      instance = this
    }
    // 静态方法，用于获取蓝牙管理器的实例
    static getInstance() {
      if (!instance) {
        instance = new Device()
      }
      return instance
    }

    // ArrayBuffer转16进度字符串示例
    ab2hex(buffer) {
      var hexArr = Array.prototype.map.call(new Uint8Array(buffer), function (bit) {
        return ('00' + bit.toString(16)).slice(-2)
      })
      // console.log('ab2hex: ', hexArr)
      return hexArr
    }

    // 启用 notify 功能
    listenNotify(opt) {
      return new Promise((resolve, reject) => {
        // 预防重复启动
        const isNotify = this.optsNotify.some((item) => JSON.stringify(item) == JSON.stringify(opt))
        if (isNotify) {
          console.log('已经启用过 notify 功能')
          resolve()
        } else {
          uni.notifyBLECharacteristicValueChange({
            state: true, // 启用 notify 功能
            ...opt,
            success: (message) => {
              console.log('成功启用 notify 功能', opt)
              this.optsNotify.push(opt)
              // 启动一次notify，就加1
              // this.notifyCount = this.notifyCount + 1
              console.log('listenNotify：', message)
              //监听设备发送数据
              uni.onBLECharacteristicValueChange((res) => {
                const resData = this.ab2hex(res.value)
                // console.log('listenNotify Change：', res)
                console.log('listenNotify resData', resData)
                uni.$emit('notify', resData)
              })
              resolve()
            },
            fail: (e) => {
              console.log('listen err', e)
              reject(new Error(e))
            },
          })
        }
      })
    }
  }
  return Device
})()
/**
 * 连接设备
 * @param {Object} device 设备信息
 * @param {String} device.deviceId 设备id
 * @param {String} device.name 设备名称
 * @param {String} device.rssi 设备信号强度
 * @param {String} device.advertisData 设备广播数据
 * @param {String} device.advertisServiceUUIDs 设备服务UUID
 */
async function connectDevice(device) {
  await uni.openBluetoothAdapter()
  // 打开蓝牙适配器
  return new Promise((resolve, reject) => {
    uni.showLoading({ title: '连接中', mask: true })
    let { deviceId } = device
    // 连接蓝牙
    uni.createBLEConnection({
      deviceId,
      timeout: 10000,
      success: async function (res) {
        console.log('res', res)

        // 获取蓝牙特征
        await getCharacteristicId(deviceId)
      },
      fail: function (error) {
        console.log('createBLEConnection error', error)
        // uni.showToast({ title: '连接失败',icon: 'error' })
        reject()
      },
      complete: async function () {
        uni.hideLoading()
        await uni.stopBluetoothDevicesDiscovery()
      },
    })
  })
}

async function onNotify() {
  // const deviceClass = new DeviceClass()
  // await deviceClass.listenNotify(opts.notify)

  uni.notifyBLECharacteristicValueChange({
    state: true, // 启用 notify 功能
    ...opts.notify,
    success: (message) => {
      console.log('listenNotify：', message)
    },
    fail: (e) => {
      console.log('listen err', e)
    },
  })
}

function onListen() {
  //监听设备发送数据
  uni.onBLECharacteristicValueChange((res) => {
    console.log('onListen', res)
    // uni.offBLECharacteristicValueChange()
    const resData = ab2hex(res.value)
    console.log('listenNotify resData', JSON.stringify(resData))
    // uni.$emit('notify', resData)
  })
}
// 获取蓝牙特征
function getCharacteristicId(deviceId) {
  return new Promise((resolve, reject) => {
    uni.showLoading({ title: '获取特征中...' })
    let time = 3
    const opts = {}
    async function get() {
      time--
      // 获取蓝牙设备的所有服务
      const { services /* , errMsg */ } = await uni.getBLEDeviceServices({ deviceId })
      console.log('6 蓝牙特征服务ID', services)
      if (services.length <= 0) {
        if (time < 0) {
          uni.showToast({ title: '未找到读写特征值', icon: 'none' })
          reject('未找到读写特征值')
          return
        }
        setTimeout(get, 1000)
        return
      }
      for (let i = 0; i < services.length; i++) {
        const serviceId = services[i].uuid
        // 获取蓝牙设备指定服务中所有特征值
        let characteristics = await uni.getBLEDeviceCharacteristics({ deviceId, serviceId })
        console.log('7 蓝牙特征', characteristics.characteristics)
        // if (!opts.write) {
        //   // 找有写权限的特征ID
        //   let canWrite = characteristics.characteristics.find((v) => v.properties.write == true)
        //   if (canWrite) {
        //     opts.write = {
        //       characteristicId: canWrite.uuid,
        //       deviceId,
        //       serviceId,
        //     }
        //     // console.log('8 找到有写入权限的特征', canWrite)
        //   }
        // }

        // if (!opts.notify) {
        //   // 找有notify 权限的特征ID
        //   let canNotify = characteristics.characteristics.find((v) => v.properties.notify == true)
        //   if (canNotify) {
        //     opts.notify = {
        //       characteristicId: canNotify.uuid,
        //       deviceId,
        //       serviceId,
        //     }
        //     // console.log('9 找到有 读 和 notify 权限的特征', canNotify)
        //   }
        // }
        // if (opts.write && opts.notify) break
      }

      // console.log('opts', opts)
      // uni.hideLoading()
      resolve(opts)
      return
    }
    setTimeout(get, 1000)
  })
}
// const cmd = ref('06 63 0C 01 01 01 01 01 00 00 00 FF FF FF FF')
// 01 10 06 49 00 03 06 00 00 00 42 00 32
const cmd = ref('01 10 06 49 00 03 06 00 00 00 0A 00 6E')
const data = ref(null)
function send() {
  // const off = '01 03 00 12 00 01' /*01 06 00 02 00 00 28 0A '' */
  const c = cmd.value.split(' ')
  c.push(...crc16modbus(c))
  console.log('指令', c)

  // const buffer = arrayToBuffer(c)
  sendToDevice(c, () => {
    uni.$once('notify', (res) => {
      console.log('data:', JSON.stringify(res))
      data.value = res
    })
  })
}
// 心跳定时器
let heartbeatTimer = null
let num = 0
async function getData() {
  // // 发送心跳，读取数据
  // function sendHeartbeat() {
  //   clearInterval(heartbeatTimer)
  //   heartbeatTimer = setInterval(() => {
  //     num++
  //     if (num == 1000) {
  //       clearInterval(heartbeatTimer)
  //       num = 0
  //     } else {
  //       send()
  //     }
  //   }, 2000)
  // }
  // //发出心跳数据
  // sendHeartbeat()
  // // send()

  // uni.$on(uni.$myNotifyEventName, (data) => {
  //   console.log('data:', data)
  //   // uni.$off(uni.$myNotifyEventName)
  // })
  cmd.value = '01 06 00 08 00 01'
  send()
}
async function setData() {
  cmd.value = '01 10 06 49 00 03 06 00 00 00 14 00 6E'
}
function arrayToBuffer(arr) {
  let cmd = arr.map((v) => '0x' + v)
  // console.log("arrayToBuffer", cmd)
  let uint16arr = new Uint8Array(cmd)
  return uint16arr.buffer //返回由 Uint8Array引用的 ArrayBuffer
}
// CRC-16/MODBUS 校验
function crc16modbus(data) {
  const dataBytes = data.map((v) => parseInt(v, 16))

  let crc = 0xffff
  const polynomial = 0xa001 // This is the polynomial x^16 + x^15 + x^2 + 1

  for (const byte of dataBytes) {
    crc ^= byte
    for (let i = 0; i < 8; i++) {
      if (crc & 0x0001) {
        crc = ((crc >> 1) ^ polynomial) & 0xffff
      } else {
        crc >>= 1
      }
    }
  }

  return crc.toString(16).toUpperCase().padStart(4, '0').match(/\w{2}/g).reverse()
}
/**
 * 发送至设备
 * @param {Object} opt 设备特征值
 * @param {String} value 指令
 * @param {Function} callback 回调函数
 */
function sendToDevice(value, callback) {
  return new Promise((resolve, reject) => {
    // console.log('send', opt, value)
    // let value = uni.$utils.cmd.getSendData();
    uni.writeBLECharacteristicValue({
      ...opts.write,
      value,
      success(res) {
        console.log('success  指令发送成功', res)
        callback && callback()
        resolve(res)
      },
      fail: function (res) {
        console.log('fail: writeBLECharacteristicValue fail', res)
        reject()
      },
    })
  })
}
onLoad(() => {
  uni.addInterceptor('onBLEConnectionStateChange', {
    invoke: (params) => {
      console.log('params', params.value)
      return false
    },
    returnValue: (res) => {
      console.log('res', res)
      return false
    },
    complete: (complete) => {
      console.log('complete', complete)
      uni.onBLEConnectionStateChange(function (res) {
        // 该方法回调中可以用于处理连接意外断开等异常情况
        console.log(`device ${res.deviceId} state has changed, connected: ${res.connected}`)
      })
    },
    success: (success) => {
      console.log('success', success)
    },
    fail: (fail) => {
      console.log('fail', fail)
    },
  })
})
uni.addInterceptor('writeBLECharacteristicValue', {
  invoke: (params) => {
    // console.log('params', params.value)
    params.value = arrayToBuffer(params.value)
  },
})
// 无效
uni.addInterceptor('onBLEConnectionStateChange', {
  invoke: (params) => {
    console.log('params', params.value)
    return false
  },
  returnValue: (res) => {
    console.log('res', res)
    return false
  },
  complete: (complete) => {
    console.log('complete', complete)
  },
  success: (success) => {
    console.log('success', success)
  },
  fail: (fail) => {
    console.log('fail', fail)
  },
})
</script>

<style lang="scss" scoped></style>
